package il.ac.tau.cg.ex3.math.shapes;

import il.ac.tau.cg.ex3.math.Params;
import il.ac.tau.cg.ex3.math.Point;
import il.ac.tau.cg.ex3.math.Vector;
import il.ac.tau.cg.ex3.utils.octree.BoundingBox;

import java.util.SortedSet;
import java.util.TreeSet;

public class Sphere extends Shape
{
	private Point center;
	private double radius;
	
	public Sphere(Point center, double radius)
	{
		this.center = center;
		this.radius = radius;
	}

	public void setCenter(Point center)
	{
		this.center = center;
	}
	
	public Point getCenter()
	{
		return center;
	}
	
	public void setRadius(double radius)
	{
		this.radius = radius;
	}
	
	public double getRadius()
	{
		return radius;
	}

	@Override
	public SortedSet<Double> intersect(Point origin, Vector direction)
	{
		SortedSet<Double> result = new TreeSet<Double>();
		
		double dl = direction.length();
		
		Vector c = new Vector(origin, center);
		Vector l = direction.scale(1.0 / dl);
		
		double lc = l.dot(c);
		double d = lc * lc - c.dot(c) + radius * radius;
		
		if (d < 0.0)
		{
			return result;
		}
		
		double t1 = lc + Math.sqrt(d);
		double t2 = lc - Math.sqrt(d);
		
		if (t1 >= 0.0)
		{
			result.add(t1 / dl);
		}
		
		if (t2 >= 0.0)
		{
			result.add(t2 / dl);
		}
		
		return result;
	}

	@Override
	public Vector getNormal(Point p)
	{
		return new Vector(center, p).normalize();
	}

	@Override
	public Params getParams(Point p)
	{
		Vector vec = new Vector(center, p).normalize();
		
		Vector ex = new Vector(1.0, 0.0, 0.0);
		Vector ey = new Vector(0.0, 1.0, 0.0);
		Vector ez = new Vector(0.0, 0.0, 1.0);
		
		Vector ortho = ez.cross(vec.cross(ez)).normalize();
		
		double c = ortho.dot(ex);
		double s = Math.acos(ortho.dot(ey));
		
		double u = Math.acos(vec.dot(ez)) / Math.PI;
		double v = (Math.PI + ((c > 0.0) ? s : -s)) / (2.0 * Math.PI);
		
		u = Math.min(Math.max(u, 0.0), 1.0);
		v = Math.min(Math.max(v, 0.0), 1.0);
		
		return new Params(u, v);
	}

	@Override
	public BoundingBox getBoundingBox()
	{
		Vector vx = new Vector(1.0, 0.0, 0.0);
		Vector vy = new Vector(0.0, 1.0, 0.0);
		Vector vz = new Vector(0.0, 0.0, 1.0);
		
		double minX = center.add(vx.scale(radius).negate()).getX();
		double maxX = center.add(vx.scale(radius)).getX();
		double minY = center.add(vy.scale(radius).negate()).getY();
		double maxY = center.add(vy.scale(radius)).getY();
		double minZ = center.add(vz.scale(radius).negate()).getZ();
		double maxZ = center.add(vz.scale(radius)).getZ();
		
		return new BoundingBox(minX, maxX, minY, maxY, minZ, maxZ);
	}
}
